/**
 * 
 */
package cn.cityhouse.avm.regression;

import org.apache.commons.math3.exception.NullArgumentException;
import org.apache.commons.math3.ml.distance.EuclideanDistance;

import cn.cityhouse.avm.utils.GISUtils;

/**
 * This class is implemented for computing geographic or projected distance for
 * model
 * 
 * @author <a href="mailto:wangcl@lreis.ac.cn">ChenLiang Wang</a>
 *
 */
public class GWR_dist {
	private double[][] dp_locat = null;
	private double[][] rp_locat = null;
	private double[][] dist_res = null;
	int focus = -1;
	boolean isLongLat = false;
	boolean isRp_given = false;

	/**
	 * @param dp_locat
	 *            numeric vector of two colunms, coordinates of data points
	 */
	public GWR_dist(double[][] dp_locat) {
		super();
		if (dp_locat.length <= 0)
			throw new NullArgumentException();
		this.dp_locat = dp_locat;
		this.rp_locat = dp_locat;
		this.isRp_given = false;
		this.isLongLat = false;
		this.focus = 0;
	}

	/**
	 * @param dp_locat
	 *            numeric vector of two colunms, coordinates of data points
	 * @param rp_locat
	 *            numeric vector of two colunms, coordinates of regression
	 *            pointss
	 */
	public GWR_dist(double[][] dp_locat, double[][] rp_locat) {
		super();
		if (dp_locat.length <= 0)
			throw new NullArgumentException();
		this.dp_locat = dp_locat;
		this.isLongLat = false;
		if (rp_locat != null) {
			this.rp_locat = rp_locat;
			this.isRp_given = true;
		} else {
			this.rp_locat = dp_locat;
			this.isRp_given = false;
		}
		this.focus = 0;
	}

	/**
	 * @param dp_locat
	 *            numeric vector of two colunms, coordinates of data points
	 * @param rp_locat
	 *            numeric vector of two colunms, coordinates of regression
	 *            points
	 * @param focus
	 *            an integer, indexing to the current regression point, if
	 *            focus=0, all the distances between all the regression points
	 *            and data points will be calculated and a distance matrix will
	 *            be returned; if 0&lt;focus&lt;length(rp.locat), then the
	 *            distances between the focusth regression points and data
	 *            points will be calculated and a distance vector will be
	 *            returned;
	 * 
	 */
	public GWR_dist(double[][] dp_locat, double[][] rp_locat, int focus) {
		super();
		if (rp_locat.length <= 0 | dp_locat.length <= 0)
			throw new NullArgumentException();
		this.dp_locat = dp_locat;
		this.rp_locat = rp_locat;
		this.focus = focus;
		this.isRp_given = true;
		this.isLongLat = false;
	}

	/**
	 * @param dp_locat
	 *            numeric vector of two colunms, coordinates of data points
	 * @param rp_locat
	 *            numeric vector of two colunms, coordinates of regression
	 *            points
	 * @param focus
	 *            an integer, indexing to the current regression point, if
	 *            focus=0, all the distances between all the regression points
	 *            and data points will be calculated and a distance matrix will
	 *            be returned; if 0&lt;focus&lt;length(rp.locat), then the
	 *            distances between the focusth regression points and data
	 *            points will be calculated and a distance vector will be
	 *            returned;
	 * @param isLongLat
	 *            if TRUE, great circle will be caculated
	 */
	public GWR_dist(double[][] dp_locat, double[][] rp_locat, int focus, boolean isLongLat) {
		super();
		if (rp_locat.length <= 0 | dp_locat.length <= 0)
			throw new NullArgumentException();
		this.dp_locat = dp_locat;
		this.rp_locat = rp_locat;
		this.focus = focus;
		this.isLongLat = isLongLat;
		this.isRp_given = false;
	}

	double[] computeDist() {
		if (this.dp_locat == null | this.rp_locat == null)
			throw new NullArgumentException();
		if (this.rp_locat != null) {
			this.isRp_given = true;
		} else {
			this.isRp_given = false;
			this.rp_locat = this.dp_locat;
		}
		int n_rp = rp_locat.length;
		int n_dp = dp_locat.length;
		double[] dist_res2 = new double[n_dp];
		if (this.focus > -1) {
			// ####Calculate a distance vector at regression point "focus"
			if (this.isLongLat) {
				System.out.println("Warning: Using geograhpic CRS.....");
				for (int i = 0; i < n_dp; i++) {
					dist_res2[i] = GISUtils.distance_84(dp_locat[i], rp_locat[focus]);// (dp_locat[i],
																						// rp_locat[focus]);
				}
			} else {
				EuclideanDistance edist = null;
				for (int i = 0; i < n_dp; i++) {
					edist = new EuclideanDistance();
					dist_res2[i] = edist.compute(dp_locat[i], rp_locat[focus]);
					// dist_res2[i] = GISUtils.distance(dp_locat[i],
					// rp_locat[focus]);
					// System.out.print(dist_res[i][0]+" ");
				}
			}
		}
		return dist_res2;
	}

	public double[][] getDp_locat() {
		return dp_locat;
	}

	public double[][] getRp_locat() {
		return rp_locat;
	}

	public void setDp_locat(double[][] dp_locat) {
		this.dp_locat = dp_locat;
	}

	public void setRp_locat(double[][] rp_locat) {
		this.rp_locat = rp_locat;
	}
}